package edu.pitt.myptm.datamanager;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.ArrayList;

import edu.pitt.myptm.DataItem;

/*Implementing read and write using slotted page*/

public class MyFileOperator {
	public boolean deleteFile(String filename) {
		File f = new File(filename);
		return f.delete();
	}
	public boolean deleteFile() {
		File f = new File(myfilename);
		try {
			if(rdf != null)
			rdf.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return f.delete();
	}
	
	private String myfilename;
	private int itemSize;
	private int pageLimit;
	
	public int getPageLimit() {
		return pageLimit;
	}
	
	public void setPageLimit(int pageLimit) {
		this.pageLimit = pageLimit;
	}
	
	public String getMyfilename() {
		return myfilename;
	}
	public void setMyfilename(String myfilename) {
		this.myfilename = myfilename;
	}
	
	public void setItemSize(int num) {
		itemSize = num;
	}
	
	private RandomAccessFile rdf;
	public boolean createRDF() {
		if(rdf == null) {
		try {
			rdf = new RandomAccessFile(myfilename,"rw");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return true;
		}
		return true;
	}
	
	
	//read results, if the file does not exist, return null
	public DataItem readResult(int pageNum,int id) {
		File f = new File(myfilename);
		if(!f.exists()) {
			return null;
		} else {
			createRDF();
		}
		int idx;
		try {
			idx = readPointer(pageNum,id);
			if(idx == -1) {
				return null;
			} else {
				int start = 4+ pageNum * 512 + idx * itemSize;
				rdf.seek(start);
				DataItem item = new DataItem();
				item.setId(rdf.readInt());
				byte[] clientBytes = new byte[DataItem.clientLength];
				rdf.readFully(clientBytes);
				byte[] phoneBytes = new byte[DataItem.phoneLength];
				rdf.readFully(phoneBytes);
				item.setClientName(clientBytes);
				item.setPhone(phoneBytes);
				return item;
			}
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
			return null;
		}
		
	}
	//read in reverse to find the pointer to the item
	public int readPointer(int pageNum, int id) throws IOException {
		if(rdf==null) createRDF();
		int endIndex = 512 * (pageNum + 1) - 4;
		int pageStart = 512 * pageNum;
		rdf.seek(pageStart);
		int count = rdf.readInt();
		for(int i = 0;i<count;i++) {
			rdf.seek(endIndex);
			int currentid = rdf.readInt();
			if (currentid == id)
				return i;
			endIndex = endIndex - 4;
		}
		return -1;
	}
	
	public int myhash(int id) {
		return id%pageLimit;
	}
	/*
	public boolean writeRecord(DataItem d, int pageNum) {
		try {
			if(rdf==null) createRDF();
			int pagehead = pageNum*512;
			rdf.seek(pagehead);
			int count = rdf.readInt();
			if(count >= 14) {
				return false;
			}
			int pointerIndex = pagehead + 512 - (count+1) * 4;
			int recordIndex = pagehead + count * itemSize + 4;
			rdf.seek(pointerIndex);
			rdf.writeInt(d.getId());
			rdf.seek(recordIndex);
			rdf.writeInt(d.getId());
			rdf.write(d.getClientName());
			rdf.write(d.getPhone());
			count = count + 1;
			rdf.seek(pagehead);
			rdf.writeInt(count);
			return true;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
	}*/
	public boolean writeNullItem(int id,int pageNum) {
		try {
			if(rdf==null) createRDF();
			int pagehead = pageNum * 512;
			rdf.seek(pagehead);
			int count = rdf.readInt();
			
			int endIndex = 512 * (pageNum + 1) - 4;
			int start = 4 + 512 * pageNum ;
			
			for(int i = 0;i<count;i++) {
				rdf.seek(endIndex);
				int currentid = rdf.readInt();
				if(currentid == id) {
					rdf.seek(endIndex);
					rdf.writeInt(0);
					return true;
				}
				endIndex = endIndex - 4;
				start = start + itemSize;
			}
			return false;
		}catch(IOException e) {
			e.printStackTrace();
			return false;
		}
	}
	
	public boolean writeRecord(DataItem d,int pageNum) {
		try {
			if(rdf==null) createRDF();
			int pagehead = pageNum * 512;
			rdf.seek(pagehead);
			int count = rdf.readInt();
			
			int id = d.getId();
			int endIndex = 512 * (pageNum + 1) - 4;
			int start = 4 + 512 * pageNum ;
			
			for(int i = 0;i<count;i++) {
				rdf.seek(endIndex);
				int currentid = rdf.readInt();
				if(currentid == id) {
					rdf.seek(endIndex);
					rdf.writeInt(d.getId());
					rdf.seek(start);
					rdf.writeInt(d.getId());
					rdf.write(d.getClientName());
					rdf.write(d.getPhone());
					return true;
				}
				endIndex = endIndex - 4;
				start = start + itemSize;
			}
			if(count >= 14) {
				return false;
			} else {
				rdf.seek(endIndex);
				rdf.writeInt(d.getId());
				rdf.seek(start);
				rdf.writeInt(d.getId());
				rdf.write(d.getClientName());
				rdf.write(d.getPhone());
				count ++ ;
				rdf.seek(pagehead);
				rdf.writeInt(count);
				return true;
			}
		}catch(IOException e) {
			e.printStackTrace();
			return false;
		}
	}
	
	
	public boolean createNewFile() {
		File f = new File(myfilename);
		if(!f.exists()) {
			try {
				f.createNewFile();
				createRDF();
				byte[] blank = new byte[508];
				for(int i = 0;i<pageLimit;i++) {
					rdf.writeInt(0);
					rdf.write(blank);
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return false;
			}
		}
		return true;
	}
	
	public boolean forceCreateFile() {
		File f = new File(myfilename);
		if(f.exists()) f.delete();
		if(!f.exists()) {
			try {
				f.createNewFile();
				createRDF();
				byte[] blank = new byte[508];
				for(int i = 0;i<pageLimit;i++) {
					rdf.writeInt(0);
					rdf.write(blank);
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return false;
			}
		}
		return true;
	}
	//Write the entire page into disk
	public boolean writePage(byte[] page,int pageNo) {
		boolean success = true;
		File f = new File(myfilename);
		if(!f.exists()) {
			try {
				f.createNewFile();
				createRDF();
				byte[] blank = new byte[508];
				for(int i = 0;i<pageLimit;i++) {
					rdf.writeInt(0);
					rdf.write(blank);
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				success = false;
				return false;
			}
		}
		try {
			rdf.seek(pageNo*512);
			rdf.write(page);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return success;
	}
	
	//write data item into file, if file does not exist, create the file
	public boolean writeItem(DataItem d) {
		boolean success = true;
		File f = new File(myfilename);
		if(!f.exists()) {
			try {
				f.createNewFile();
				createRDF();
				byte[] blank = new byte[508];
				for(int i = 0;i<pageLimit;i++) {
					rdf.writeInt(0);
					rdf.write(blank);
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				success = false;
				return false;
			}
		}
		int pageNum = myhash(d.getId());
		boolean isInserted = false;
		int total = 0;
		while(!isInserted) {
			total++;
			if(total>pageLimit) return false;
			isInserted = writeRecord(d,pageNum);
			pageNum = pageNum + 1;
		}
		return true;
	}
	
	public DataItem readItem(int id) {
		int pageNum = myhash(id);
		boolean isFound = false;
		int total = 0;
		DataItem d = null;
		while(!isFound) {
			total ++;
			if(total > pageLimit) return null;
			d = readResult(pageNum,id);
			if(d == null) {
				isFound = false;
				pageNum = pageNum + 1;
				pageNum = pageNum % pageLimit;
			} else {
				isFound = true;
			}
		}
		return d;
	}
	
	public void searchPage(int pageNo,ArrayList<DataItem> items,String areacode) {
		int pageStart = pageNo*512;
		char sa = areacode.charAt(0);
		char sb = areacode.charAt(1);
		char sc = areacode.charAt(2);
		
		try {
			if(rdf==null) createRDF();
			rdf.seek(pageStart);
			int count = rdf.readInt();
			for(int i = 0;i<count;i++) {
				int areaStart = i*34+26+pageStart;
				rdf.seek(areaStart);
				byte e = rdf.readByte();
				
				char a = (char)e;
				char b = (char)(rdf.readByte());
				char c = (char)(rdf.readByte());
				//char b = rdf.readChar();
				//char c = rdf.readChar();
				if((a == sa)&&(b==sb)&&(c==sc)) {
					int recordStart = 4+pageStart+i*itemSize;
					rdf.seek(recordStart);
					DataItem item = new DataItem();
					item.setId(rdf.readInt());
					byte[] clientBytes = new byte[DataItem.clientLength];
					rdf.readFully(clientBytes);
					byte[] phoneBytes = new byte[DataItem.phoneLength];
					rdf.readFully(phoneBytes);
					item.setClientName(clientBytes);
					item.setPhone(phoneBytes);
					int pointerStart = pageStart + 512- 4 * i;
					rdf.seek(pointerStart);
					int pointerid = rdf.readInt();
					if(item.getId() == pointerid)
					items.add(item);
				}
				
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
	
	public ArrayList<DataItem> matchItem(String areacode) {
		ArrayList<DataItem> items = new ArrayList<DataItem>();
		for(int i = 0;i<pageLimit;i++) {
			searchPage(i,items,areacode);
		}
		return items;
	}
	
	public byte[] readPage(int pageNo) {
		byte[] page = new byte[512];
		int pageStart = pageNo*512;
		try {
			if(rdf==null) createRDF();
			rdf.seek(pageStart);
			rdf.read(page);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return page;
	}
}
